#pragma once
#include<iostream>
#include<pthread.h>
#include<unistd.h>
#include<vector>
#include <semaphore.h>

template<class T>
class RingQueue
{
public:
    RingQueue(int maxcap=10)
        : maxcap_(maxcap)
    {
        queue_.resize(maxcap_); 
        pthread_mutex_init(&p_mutex, nullptr); 
        pthread_mutex_init(&c_mutex, nullptr); 
        sem_init(&p_sem, 0, maxcap_); // 初始化生产者信号量，初始值为最大容量
        sem_init(&c_sem, 0, 0); // 初始化消费者信号量，初始值为0
    }

    void P(sem_t* sem)
    {
        sem_wait(sem); // 等待信号量
    }

    void V(sem_t* sem)
    {
        sem_post(sem); // 释放信号量
    }

    void push(const T& data)
    {
        P(&p_sem); // 等待生产者信号量，如果队列已满，则阻塞等待
        pthread_mutex_lock(&p_mutex); // 加锁，将P操作放在前面有两个原因：1.临界资源越少越好；2.PV操作本身是原子操作，没有必要加锁
        queue_[p_index] = data;
        p_index = (p_index + 1) % maxcap_; 
        V(&c_sem); // 释放消费者信号量
        pthread_mutex_unlock(&p_mutex); // 解锁生产者互斥锁
    }
    //加锁的目的是为了防止多个生产者或者多个消费者同时操作队列导致数据不一致，生产者和消费者可以同时访问，支持并发
    void pop(T& data)
    {
        P(&c_sem); // 等待消费者信号量，如果队列为空，则阻塞等待
        pthread_mutex_lock(&c_mutex); 
        data = queue_[c_index];
        c_index = (c_index + 1) % maxcap_;
        V(&p_sem); // 释放生产者信号量
        pthread_mutex_unlock(&c_mutex); // 解锁消费者互斥锁
    }
    ~RingQueue()
    {
        pthread_mutex_destroy(&p_mutex);
        pthread_mutex_destroy(&c_mutex);
        sem_destroy(&p_sem);
        sem_destroy(&c_sem);
    }
private:
    std::vector<T> queue_;
    int maxcap_;
    int p_index=0;
    int c_index=0;

    sem_t p_sem;//生产者信号量
    sem_t c_sem;//消费者信号量

    pthread_mutex_t p_mutex;//生产者互斥锁
    pthread_mutex_t c_mutex;//消费者互斥锁
};

std::string opers="+-*/%";

enum{
    DivZero=1,
    ModZero,
    Unknown
};

class Task
{
public:
    Task(int x, int y, char op) : data1_(x), data2_(y), oper_(op), result_(0), exitcode_(0)
    {
    }

    Task(const Task& other)
        : data1_(other.data1_), data2_(other.data2_), oper_(other.oper_), result_(other.result_), exitcode_(other.exitcode_)
    {
    }

    void run()
    {
        switch (oper_)
        {
            case '+':
                result_ = data1_ + data2_;
                break;
            case '-':
                result_ = data1_ - data2_;
                break;
            case '*':
                result_ = data1_ * data2_;
                break;
            case '/':
            {
                if(data2_ == 0) exitcode_ = DivZero;
                else result_ = data1_ / data2_;
            }
                break;
            case '%':
            {
                if(data2_ == 0) exitcode_ = ModZero;
                else result_ = data1_ % data2_;
            }            break;
            default:
                exitcode_ = Unknown;
                break;
        }
    }
    void operator ()()
    {
        run();
    }
    std::string GetResult()
    {
        std::string r = std::to_string(data1_);
        r += oper_;
        r += std::to_string(data2_);
        r += "=";
        r += std::to_string(result_);
        r += "[code: ";
        r += std::to_string(exitcode_);
        r += "]";

        return r;
    }
    std::string GetTask()
    {
        std::string r = std::to_string(data1_);
        r += oper_;
        r += std::to_string(data2_);
        r += "=?";
        return r;
    }
    ~Task()
    {
    }
private:
    int data1_;
    int data2_;
    char oper_;

    int result_;
    int exitcode_;
};

